package net.paoding.rose.jade.statement.jexl;

import net.paoding.rose.jade.excetion.JadeException;
import net.paoding.rose.jade.statement.jexl.internal.introspection.RosePermissions;
import net.paoding.rose.jade.statement.jexl.internal.introspection.RoseUberspect;
import org.apache.commons.jexl3.JexlBuilder;
import org.apache.commons.jexl3.JexlContext;
import org.apache.commons.jexl3.JexlEngine;
import org.apache.commons.jexl3.JexlScript;
import org.apache.commons.jexl3.MapContext;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 执行 jexl3 解析 转换成 sql
 * @author fusheng.zhang
 * @date 2022-03-08 10:36:33
 */
public class Jexl3Execute {

    private int index;

    private final Map<String, Object> params;

    private final Map<String, ?> constantParams;

    private final List<Object> paramValues;

    private final StringBuilder result;

    private final JexlEngine jexlEngine;

    private final JexlContext jexlContext;

    public Jexl3Execute(
            Map<String, Object> params, Map<String, ?> constantParams) {
        this.params = CollectionUtils.isEmpty(params) ? new HashMap<>(0) : params;
        this.constantParams = CollectionUtils.isEmpty(constantParams) ? new HashMap<>(0) : constantParams;
        this.paramValues = new ArrayList<>(params.size());
        this.result = new StringBuilder();

        this.jexlContext = new MapContext();
        JexlBuilder jexlBuilder = new JexlBuilder();
        jexlBuilder.namespaces(Jexl3Constant.JEXL3_BUILDER_NAME_SPACE);
        jexlBuilder.strict(false).safe(false);
        jexlBuilder.permissions(RosePermissions.JEXL_PERMISSIONS);
        jexlBuilder.uberspect(new RoseUberspect(jexlBuilder.permissions()));
        this.jexlEngine = jexlBuilder.create();
    }

    public Jexl3Execute setValue(Object value) {
        this.paramValues.add(value);
        return this;
    }

    /**
     * 执行表达式
     * @param params         参数列表
     * @param constantParams 常量参数列表
     * @param scriptText     执行脚本
     * @return
     */
    public Object execute(Map<String, Object> params, Map<String, ?> constantParams, String scriptText) {
        try {
            JexlScript jexlScript = this.jexlEngine.createScript(scriptText);
            this.jexlContext.set(Jexl3Constant.VAR_PREFIX, params);
            this.jexlContext.set(Jexl3Constant.CONST_PREFIX, constantParams);
            return jexlScript.execute(jexlContext);
        } catch (Exception e) {
            throw new JadeException("{}->{}", e, scriptText, e.getMessage());
        }
    }

    public void setStr(String str) {
        this.result.append(str);
    }

    public void setParam(String key, Object value) {
        this.params.put(key, value);
    }

    public String getResult() {
        return result.toString();
    }

    public List<Object> getArgs() {
        return this.paramValues;
    }

    public int getIndex() {
        return index;
    }

    public void setIndex(int index) {
        this.index = index;
    }

    public Map<String, Object> getParams() {
        return params;
    }

    public Map<String, ?> getConstantParams() {
        return constantParams;
    }

    public List<Object> getParamValues() {
        return paramValues;
    }
}
